%%%-------------------------------------------------------------------
%%% @author Rain
%%% @doc 游戏ranch协议处理
%%% @end
%%%-------------------------------------------------------------------
-module(game_protocol).

-behavior(ranch_protocol).
-behaviour(gen_server).
-include("login_pb.hrl").
-include("common.hrl").
-include("network.hrl").
-include("lock_step_pb.hrl").

-export([start_link/4]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
    code_change/3]).

-export([async_recv/2, async_recv_body/2, async_recv_head/1]).
-export([unpack_net_bin/1]).
-export([deal_gate_msg/2]).
-export([gate_to_client_msg/2]).

%%%===================================================================
%%% Spawning and gen_server implementation
%%%===================================================================

%% @doc 启动协议进程ranch_protocol回调
start_link(_Ref, _Socket, _Transport, _ProtocolOptions) ->
    {ok, Pid} = gen_server:start_link(?MODULE, [], []),
    {ok, Pid}.

init([]) ->
    network_tick(),
    {ok, #net_state{last_heart_beat_ts = game_util_time:now_sec()}}.

handle_call(_Request, _From, NetState = #net_state{}) ->
    {reply, ok, NetState}.

handle_cast(_Request, NetState = #net_state{}) ->
    {noreply, NetState}.

%% ranch_conns_sup传来的握手数据
handle_info({handshake, Ref, Transport, CSocket, _HandshakeTimeout},
    NetState = #net_state{net_step = ?NET_STEP_INIT}) ->
    self() ! client_first_pack,
    {noreply, NetState#net_state{socket = CSocket, transport = Transport,
                                 ranch_ref = Ref, net_step = ?NET_STEP_HANDSHAKE}};

%% 客户端首次连接包
handle_info(client_first_pack, #net_state{net_step = ?NET_STEP_HANDSHAKE} = NetState) ->
    #net_state{transport = Transport, socket = Socket} = NetState,
    try
        {ok, FirstPack} = Transport:recv(Socket, 0, ?NETWORK_RECV_TIMEOUT),
        NetState1 = #net_state{} = client_first_pack(FirstPack, NetState),
        {noreply, NetState1#net_state{net_step = ?NET_STEP_FIRST_PACK_DONE}}
    catch
        Class:Error:Stack  ->
            ?LOG_ERROR("client_first_pack fail ~p Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            {stop, client_first_pack_fail, NetState}
    end;

% 收到异步数据包包头
handle_info({inet_async, Socket, PackRef, {ok, Data}}, #net_state{net_step = ?NET_STEP_FIRST_PACK_DONE,
    socket = Socket, pack_ref = PackRef, pack_step = ?NET_PACK_STEP_HEAD, net_mod_module = NetModModule} = NetState) ->
    NetModModule:deal_head(Data, NetState);

% 收到异步数据包包长度
handle_info({inet_async, Socket, PackRef, {ok, Data}}, #net_state{net_step = ?NET_STEP_FIRST_PACK_DONE,
    socket = Socket, pack_ref = PackRef, pack_step = ?NET_PACK_STEP_BODY_LEN, net_mod_module = NetModModule} = NetState) ->
    NetModModule:deal_body_len(Data, NetState);

% 收到异步数据包包体
handle_info({inet_async, Socket, PackRef, {ok, Data}}, #net_state{net_step = ?NET_STEP_FIRST_PACK_DONE,
    socket = Socket, pack_ref = PackRef, pack_step = ?NET_PACK_STEP_BODY, net_mod_module = NetModModule} = NetState) ->
    NetModModule:deal_body(Data, NetState);


% 收到异步数据包包体
handle_info({inet_async, Socket, PackRef1, {ok, Data}}, #net_state{net_step = NetStep,
    socket = Socket, pack_ref = PackRef2, pack_step = PackStep} = NetState) ->

    ?LOG_DEBUG("PackRef1, ~p PackRef2, ~p Data ~p PackStep ~p net_step ~p", [PackRef1, PackRef2, Data, PackStep, NetStep]),
    {noreply, NetState};


handle_info(network_tick, NetState = #net_state{net_step = ?NET_STEP_BREAK}) -> % 网络被断开
    {stop, normal, NetState};

% 网络tick - 正常
handle_info(network_tick, NetState = #net_state{}) ->
    network_tick(),
    NetState1 = loop_heart_beat_chk(NetState),
    {noreply, NetState1};

% 收到心跳
handle_info(heart_beat, NetState = #net_state{}) ->
    HeartBeatTs = game_util_time:now_sec(),
    NetState1 = NetState#net_state{last_heart_beat_ts = HeartBeatTs},
    {noreply, NetState1};

handle_info({r2c_msg, Msg}, NetState = #net_state{}) ->
    ?G2C_MSG(NetState, Msg),
    {noreply, NetState};

handle_info(Info, NetState = #net_state{}) ->
    ?LOG_DEBUG("Unknown Info ~p ", [Info]),
    {noreply, NetState}.

terminate(_Reason, _NetState = #net_state{}) ->
    ok.

code_change(_OldVsn, NetState = #net_state{}, _Extra) ->
    {ok, NetState}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

%% @doc 网络模式对应处理模块
net_mod_module(?NET_MOD_WEBSOCKET) ->
    game_protocol_ws;
net_mod_module(?NET_MOD_TCP) ->
    game_protocol_tcp.


%% @doc 客户端socket连接后首包
client_first_pack(<<"GET", _Rest/binary>> = FirstPack, #net_state{} = NetState) ->
    % HTTP/1.1 websocket激活包
    % https://en.wikipedia.org/wiki/WebSocket 维基百科websocket描述
    % 参考cowboy_websocket is_upgrade_request
    case cow_http:parse_request_line(FirstPack) of
        {<<"GET">>, _Target, 'HTTP/1.1', Rest0} -> % websocket握手
            {Headers, _Rest1} = cow_http:parse_headers(Rest0),
            {_, SecWebsocketKey} = lists:keyfind(<<"sec-websocket-key">>, 1, Headers),
            Challenge = cow_ws:encode_key(SecWebsocketKey),
            Response = io_lib:format(<<
                "HTTP/1.1 101 Switching Protocols\r\n"
                "Upgrade: websocket\r\n"
                "Connection: Upgrade\r\n"
                "Sec-WebSocket-Accept: ~s\r\n"
                "\r\n"
            >>, [Challenge]),
            NetState1 = net_mod_to_state(?NET_MOD_WEBSOCKET, NetState),
            ?G2C_MSG(NetState1, Response),
            % 启动Agent
            {ok, AgentPid} = role_svr:start_role_svr_agent(),
            % 激活gate
            AgentPid ! {active_gate, self()},
            % 开启收包 - 收握手后的第一个包包头
            NetState2 = NetState1#net_state{net_step = ?NET_STEP_FIRST_PACK_DONE, agent = AgentPid},
            async_recv_head(NetState2);
        _ ->
            ?LOG_ERROR("websocket not support ~p", [FirstPack]),
            throw(websocket_not_support)
    end;

client_first_pack(_, NetState) -> % tcp激活包
    NetState1 = net_mod_to_state(?NET_MOD_TCP, NetState),
    % 开启收包 - 收握手后的第一个包包头
    NetState2 = NetState1#net_state{net_step = ?NET_STEP_FIRST_PACK_DONE},
    async_recv_head(NetState2).


net_mod_to_state(NetMod, NetState) ->
    NetState#net_state{net_mod = NetMod, net_mod_module = net_mod_module(NetMod)}.

%% @doc 异步接收包头
async_recv_head(NetState) ->
    Length = pack_head_len(NetState#net_state.net_mod),
    {ok, NetState1} = async_recv(NetState, Length),
    NetState1#net_state{pack_step = ?NET_PACK_STEP_HEAD}.


pack_head_len(?NET_MOD_WEBSOCKET) ->
    ?WS_HEAD_LEN;
pack_head_len(?NET_MOD_TCP) ->
    ?TCP_HEAD_LEN.



%% @doc 异步接收包体
async_recv_body(NetState, PayloadLength) ->
    Length = PayloadLength + ?WS_MASK_LEN,
    {ok, NetState1 = #net_state{pack_info = PackInfo}} = async_recv(NetState, Length),
    NetState1#net_state{pack_step = ?NET_PACK_STEP_BODY, pack_info = PackInfo#{body_len => PayloadLength}}.



%% @doc 异步接受信息
async_recv(#net_state{socket = Sock} = NetState, Length) when is_port(Sock) ->
    case prim_inet:async_recv(Sock, Length, -1) of
        {error, Reason} ->
            ?LOG_ERROR("async recv error ~p~n", [{Sock, Length, Reason}]),
            {ok, NetState};
        {ok, PackRef} ->
            {ok, NetState#net_state{pack_ref = PackRef}}
    end.

%% @doc 网络tick
network_tick() ->
    erlang:send_after(?NETWORK_LOOP_TICK, self(), network_tick).


%% @doc 循环心跳检查
loop_heart_beat_chk(#net_state{last_heart_beat_ts = LastHeartBeatTs} = NetState) ->
    NowTime = game_util_time:now_sec(),
    case NowTime - LastHeartBeatTs > ?HEART_BEAT_TIMEOUT of
        ?true ->
            ?LOG_WARN("Heart Beat Timeout ~p", [NowTime - LastHeartBeatTs]),
            NetState#net_state{net_step = ?NET_STEP_BREAK};
        _ ->
            NetState
    end.



%% @doc 解包网络二进制数据包
unpack_net_bin(Bin) ->
    <<_Length:?NET_LENGTH_FIELD_SIZE, CmdMainId: ?NET_MAIN_ID_FIELD_SIZE,
        CmdSubId: ?NET_SUB_ID_FIELD_SIZE, PbBin/binary>> = Bin,
    #{pb_mod := PbMod, msg_name := PbMsgName} = proto_cmd:by_cmd(c2s, CmdMainId, CmdSubId),
    PbMod:decode_msg(PbBin, PbMsgName).

%% @doc 封包网络二进制数据包
pack_net_bin(PbRec, ?NET_MOD_TCP) ->
    pack_net_bin_base(PbRec);
pack_net_bin(PbRec, ?NET_MOD_WEBSOCKET) ->
    BinBase = pack_net_bin_base(PbRec),
    cow_ws:frame({binary, BinBase}, #{}).


pack_net_bin_base(PbRec) ->
    RecName = element(1, PbRec),
    #{pb_mod := PbMod, cmd_main_id := CmdMainId,
        cmd_sub_id := CmdSubId} = proto_cmd:by_msg_name(RecName),
    PbBin = PbMod:encode_msg(PbRec),
    Length = erlang:size(PbBin) + (?NET_MAIN_ID_FIELD_SIZE + ?NET_SUB_ID_FIELD_SIZE) div 8,
    <<Length: ?NET_LENGTH_FIELD_SIZE, CmdMainId: ?NET_MAIN_ID_FIELD_SIZE,
        CmdSubId: ?NET_SUB_ID_FIELD_SIZE, PbBin/binary>>.

%% @doc 处理网关消息
deal_gate_msg(NetState, GateMsg) ->
    case gate_handle_msg(GateMsg, NetState) of
        ok ->
            ok;
        _ ->
            gate_msg_to_agent(NetState, GateMsg)
    end.

%% @doc 将网关数据转发到agent
gate_msg_to_agent(#net_state{agent = AgentPid}, GateMsg) ->
    AgentPid ! {gate_msg, GateMsg}.

%% @doc 网关进程发送消息给客户端
gate_to_client_msg(#net_state{transport = Transport, socket = Socket, net_mod = NetMod}, GateMsg) ->
    GateMsgBin =
        case is_tuple(GateMsg) of
            ?true ->
                pack_net_bin(GateMsg, NetMod);
            _ ->
                GateMsg
        end,
    case GateMsg of
        #login_heart_s2c{} ->
            pass;
        #lock_step_one_frame_inputs_s2c{inputs = Inputs} ->
            [?LOG_DEBUG("Send Lockstep RoleId ~p Input ~p", [RoleId, Button])||
                #p_one_frame_input{role_id = RoleId,
                    input = #p_ls_input{parameters =
                    #p_ls_input_param{button = Button}}}<-Inputs, Button > 0],
            pass;
        _ ->
            ?LOG_DEBUG("send msg ~p", [GateMsg])
    end,
    Transport:send(Socket, GateMsgBin).


%% @doc 网关处理的协议数据，将会拦截不会转发给agent
%% return: ok | pass
gate_handle_msg(#login_heart_c2s{}, NetState) ->
    ?G2C_MSG(NetState, #login_heart_s2c{server_time = game_util_time:now_ms()}),
    self() ! heart_beat,
    ok;
gate_handle_msg(_, _) ->
    pass.


